﻿using System;
using System.Runtime.InteropServices;
using GUI.WAPI;

namespace WinAPI
{
    abstract partial class LibWrapper
    {
        #region structures
        //sequential layot allow us to pass 
        //this struct into Native
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        struct DriveMountInfoStruct
        {
            private Int64 fileSize;
            private Int32 fileNameLen;
            [MarshalAs(UnmanagedType.LPTStr)]
            private string fileName;
            private int pasLen;
            [MarshalAs(UnmanagedType.LPTStr)]
            private string password;

            public DriveMountInfoStruct(Int64 size, string name, string pass)
            {
                fileSize = size;
                password = pass;
                fileName = @"\??\" + name;
                fileNameLen = fileName.Length * sizeof(char);//bytes num
                pasLen = password.Length * sizeof(char);
            }
            public int Size
            {
                get { return Marshal.SizeOf(this); }
            }
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        struct PasswordStruct
        {
            [MarshalAs(UnmanagedType.LPTStr)]
            private string password;

            private Int32 passwordLen;

            public PasswordStruct(string pass)
            {
                password = pass;
                passwordLen = pass.Length * sizeof(char);//bytes num
            }

            public int Size
            {
                get { return Marshal.SizeOf(this); }
            }
        }
        #endregion

        #region Methods
        public static HandleGuard OpenVolume(char letter)
        {
            return new HandleGuard(CreateFile(
                           @"\\.\" + letter + ':',
                           GENERIC_READ | GENERIC_WRITE,
                           FILE_SHARE_WRITE | FILE_SHARE_READ,
                           NULL,
                           OPEN_EXISTING,
                           FILE_FLAG_NO_BUFFERING,
                           NULL));
        }

        public static HandleGuard OpenMasterDevice(string masterDeviceName)
        {
            return new HandleGuard(CreateFile(
                           masterDeviceName,
                           GENERIC_READ | GENERIC_WRITE,
                           FILE_SHARE_WRITE | FILE_SHARE_READ,
                           NULL,
                           OPEN_EXISTING,
                           FILE_FLAG_NO_BUFFERING,
                           NULL));
        }

        public static void CreateDevice(HandleGuard hDevice, int devNum)
        {
            uint bytesReturned = 0;
            IntPtr devNumPtr = Marshal.AllocHGlobal(sizeof(int));//memory allocation in unmanaged space
            Marshal.StructureToPtr(devNum, devNumPtr, false);//copying struct into unmanaged space
            try
            {
                if (DeviceIoControl(hDevice.DangerousGetHandle(), IOCTL_FILE_DISK_CREATE_DEVICE, devNumPtr, sizeof(int), NULL, 0, ref bytesReturned, NULL) == false)
                    throw new MarshalException();
            }
            finally
            {//Free resources
                Marshal.FreeHGlobal(devNumPtr);
            }
        }

        public static void DefineDevice(char letter, string targetPath)
        {
            if (DefineDosDevice(DDD_RAW_TARGET_PATH, letter + ":", targetPath) == false)
                throw new MarshalException();
        }

        public static void UnDefineDevice(char letter)
        {
            DefineDosDevice(DDD_REMOVE_DEFINITION, letter + ":", null);
        }

        public static void DeviceIoControl_FileOpen(HandleGuard device, string fileName, Int64 size, char letter, string password)
        {
            uint bytesReturned = 0;
            //creation of sequential structure
            DriveMountInfoStruct s = new DriveMountInfoStruct(size, fileName, password);
            IntPtr ptrStruct = NULL, intRetStr = NULL;
            try
            {
                intRetStr = Marshal.AllocHGlobal(sizeof(Int32));
                ptrStruct = Marshal.AllocHGlobal(s.Size);   //memory allocation in unmanaged space
                Marshal.StructureToPtr(s, ptrStruct, false);    //copying struct to unmanaged space
                if (DeviceIoControl(device.DangerousGetHandle(), LibWrapper.CTL_CODE(MULTI_VDD_DEVICE, 0x802, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS), ptrStruct, s.Size, intRetStr, sizeof(Int32), ref bytesReturned, NULL) == false && Marshal.GetLastWin32Error() != 32)//means drive without filesystem
                {
                    if (Marshal.GetLastWin32Error() == 1006)
                        throw new FileErrorException();//means file is wrong
                    throw new MarshalException();//other error
                }
                if(Marshal.ReadInt32(intRetStr) == 10) //new (clear) file
                    DeviceIoControl_Password(device, password, true); //true means ignoring password check
                else
                    DeviceIoControl_Password(device, password, false);
            }
            finally
            {//Free resources
                Marshal.FreeHGlobal(ptrStruct);
            }
        }

        public static void DeviceIoControl_REMOVE(HandleGuard hFile, int devNum)
        {
            IntPtr devNumPtr = NULL;
            try
            {
                uint bytesReturned = 0;
                devNumPtr = Marshal.AllocHGlobal(sizeof(int));//memory allocation in unmanaged space
                Marshal.StructureToPtr(devNum, devNumPtr, false);//copying struct into unmanaged space
                DeviceIoControl(hFile.DangerousGetHandle(), CTL_CODE(MULTI_VDD_DEVICE, 0x801, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS), devNumPtr, sizeof(int), NULL, 0, ref bytesReturned, NULL);
            }
            finally
            {//Free resources
                Marshal.FreeHGlobal(devNumPtr);
            }
        }

        public static void DeviceIoControl(HandleGuard handle, uint flags)
        {
            uint bytesReturned = 0;
            if (DeviceIoControl(handle.DangerousGetHandle(), flags, NULL, 0, NULL, 0, ref bytesReturned, NULL) == false)
                throw new MarshalException();
        }

        public static void SHChangeNotifyAddDrive(char letter)
        {
            IntPtr volumeLabelPtr = Marshal.StringToHGlobalAuto(letter + ":");
            SHChangeNotify(SCHNE_DRIVE_ADD, SCHNF_PATH, volumeLabelPtr, NULL);
            Marshal.FreeHGlobal(volumeLabelPtr);
        }

        public static void SHChangeNotifyDeleteDrive(char letter)
        {
            IntPtr volumeLabelPtr = Marshal.StringToHGlobalAuto(letter + ":");
            SHChangeNotify(SCHNE_DRIVE_REMOVED, SCHNF_PATH, volumeLabelPtr, NULL);
            Marshal.FreeHGlobal(volumeLabelPtr);
        }

        public static void DeviceIoControl_Password(HandleGuard device, string password, bool ignorePass)
        {
            uint bytesReturned = 0;
            PasswordStruct passwordStr = new PasswordStruct(password);
            IntPtr passwordStrPtr = NULL, intRetStr = NULL;
            try
            {
                passwordStrPtr = Marshal.AllocHGlobal(passwordStr.Size);//memory allocation in unmanaged space
                intRetStr = Marshal.AllocHGlobal(sizeof(Int32));
                Marshal.StructureToPtr(passwordStr, passwordStrPtr, false);//copying struct into unmanaged space
                if (DeviceIoControl(device.DangerousGetHandle(), CTL_CODE(MULTI_VDD_DEVICE, 0x806, METHOD_BUFFERED, FILE_READ_ACCESS), passwordStrPtr, passwordStr.Size, intRetStr, sizeof(Int32), ref bytesReturned, NULL) == true)
                {
                    if (Marshal.ReadInt32(intRetStr) == 0 && ignorePass == false)
                        throw new Exception(GUI.Resources.Strings.WrongPass);
                }
            }
            finally
            {
                Marshal.FreeHGlobal(intRetStr);
                Marshal.FreeHGlobal(passwordStrPtr);
            }
        }
        #endregion
    }
}